/**********************************************************
类型判断工具
**********************************************************/
var array = {
    /**
     * 判断是否是数组
     * 
     * @param {any} param 
     * @returns {Boolean}
     */
    isArray: function (param) {
        return Object.prototype.toString.call(param) === '[object Array]';
        // return typeof param === 'object' && param !== null && typeof param.length !== 'undefined';
    },
    /**
     * 将参数强转成数组格式,不带有length属性将返回null
     * 比如arguments, '123'
     * 
     * @param {any} param 
     * @param {Number} index 
     * @param {Number} count 
     * @returns {[]}
     */
    copy: function (param, index, count) {
        try {
            param.length;
        } catch (e) { return null; }
        if (typeof param.length === 'undefined') {
            return null;
        }
        index = number.toInt(index) || 0;
        count = number.toInt(count) || undefined;
        return Array.prototype.slice.call(param, index, count);
    },
    /**
     * 判断是否是String数组
     * 
     * @param {[]} param 
     * @returns {Boolean}
     */
    isStringArray: function (param) {
        if (array.isArray(param) && param.length > 0) {
            for (var i = 0; i < param.length; i++) {
                if (!string.isString(param[i])) {
                    return false;
                }
            }
            return true;
        }
        return false;
    },
    /**
     * 判断是否是Number数组
     * 
     * @param {[]} param 
     * @param {String} judge
     * @returns {Boolean}
     */
    isNumberArray: function (param, judge) {
        if (array.isArray(param) && param.length > 0) {
            for (var i = 0; i < param.length; i++) {
                if (!number.isNumber(param[i], judge)) {
                    return false;
                }
            }
            return true;
        }
        return false;
    },
    /**
     * 数组随机打散
     * 
     * @param {[]} array 
     * @returns {[]}
     */
    random: function (arr, length) {
        if (array.isArray(arr)) {
            if (!length || length < 0 || length > arr.length) {
                length = arr.length;
            }
            let i, j, t, a = [];
            Array.prototype.push.apply(a, arr);
            for (i = 0; i < length; i++) {
                j = Math.floor(Math.random() * a.length);
                if (j !== i) {
                    t = a[j];
                    a[j] = a[i];
                    a[i] = t;
                }
            }
            return length === arr.length ? a : a.slice(0, length);
        } else {
            return null;
        }
    },

    /**
     * 数组随机打散,返回下标数组
     * 
     * @param {[]} array 
     * @returns {[]}
     */
    randomIndex: function (arr, length) {
        if (array.isArray(arr)) {
            if (!length || length < 0 || length > arr.length) {
                length = arr.length;
            }
            let i, j, t, a = [];
            for (i = 0; i < arr.length; i++) {
                a[i] = i;
            }
            for (i = 0; i < length; i++) {
                j = Math.floor(Math.random() * a.length);
                if (j !== i) {
                    t = a[j];
                    a[j] = a[i];
                    a[i] = t;
                }
            }
            return length === arr.length ? a : a.slice(0, length);
        } else {
            return null;
        }
    },

    /**
     * 快速排序
     * @param  {Array} arr        [数组]
     * @param  {Function} cb      [判断回掉(值1, 值2)]
     * @return {Array}            [排好序的数组]
     */
    quickSort: function (arr, cb) {
        //如果数组<=1,则直接返回
        if (arr.length <= 1) { return arr; }
        let pivotIndex = Math.floor(arr.length / 2);
        //找基准
        let pivot = arr[pivotIndex];
        //定义左右数组
        let left = [];
        let right = [];

        //比基准小的放在left，比基准大的放在right
        for (let i = 0; i < arr.length; i++) {
            if (i !== pivotIndex) {
                if (cb) {
                    if (cb(arr[i], pivot)) {
                        left.push(arr[i]);
                    } else {
                        right.push(arr[i]);
                    }
                } else {
                    if (arr[i] <= pivot) {
                        left.push(arr[i]);
                    } else {
                        right.push(arr[i]);
                    }
                }
            }
        }
        //递归
        return array.quickSort(left, cb).concat([pivot], array.quickSort(right, cb));
    },

    /**
     * 快速排序(下标)
     * @param  {Array} arr        [数组长度]
     * @param  {Function} cb      [判断回掉(下标1, 下标2)]
     * @return {Array}            [排好序的数组]
     */
    quickSortIndex: function (length, cb) {
        var arr = [];
        for (var i = 0; i < length; i++) {
            arr.push(i);
        }
        return array.quickSort(arr, cb);
    },

    // 二分查找
    binarySearch: function (arr, value) {
        let min = 0, max = arr.length - 1;

        while (max >= min) {
            let curr = Math.floor((max + min) / 2);

            if (value > arr[curr]) {
                min = curr + 1;
            } else if (value < arr[curr]) {
                max = curr - 1;
            } else {
                return curr;
            }
        }
        return -1;
    },

    /**
     * 按权重随机，返回命中的权重数组下标
     * @param  {Array} arr        [数组]
     * @param  {Function} cb      [返回对应项权重值]
     * @return {Number}           [返回命中的下标] 
     */
    getRatioRandIndex: function (arr, cb) {
        let rArr = [];

        for (let i = 0; i < arr.length; i++) {
            if (i === 0) {
                rArr[i] = cb ? (typeof cb === 'string' ? arr[i][cb] : cb(arr[i])) : arr[i];
            } else {
                rArr[i] = rArr[i - 1] + (cb ? (typeof cb === 'string' ? arr[i][cb] : cb(arr[i])) : arr[i]);
            }
        }

        let r = number.random(1, rArr[rArr.length - 1]);

        for (let i = 0; i < rArr.length; i++) {
            if (r <= rArr[i]) {
                return i;
            }
        }

        return 0;
    }
};
var bool = {
    /**
     * 判断是否是bool类型
     * 
     * @param {any} param 
     * @returns {Boolean}
     */
    isBool: function (param) {
        return typeof param === 'boolean';
    },

    /**
     * 将参数转成bool
     * 
     * @param {any} param 
     * @returns {Boolean}
     */
    toBool: function (param) {
        return !!param;
    },

    /**
     * 随机一个bool值
     */
    random: function () {
        return Math.random() >= 0.5;
    }
};
var fun = {
    /**
     * 判断是否是方法
     * 
     * @param {function} param 
     * @returns {Boolean}
     */
    isFunction: function (param) {
        return typeof param == 'function';
    }
};
var number = {

    /**
     * 判断是否是number型的数字或字符串
     * @param {string|number} param
     * @param {(data:number)=>boolean} judge
     * @returns {Boolean}
     * 
     * exp:
     * isNumber(n);             //判断是否是数字
     * isNumber(n,'@ > 0');     //判断是否是数字并且>0
     * isNumber(n,'@ < 0');     //判断是否是数字并且<0
     * isNumber(n,'@ > 0 && @ < 100');
     * isNumber(n,'@ == 0');    //判断是否是数字并且==0
     * isNumber(n,'@ === 0');   //判断是否是数字并且===0
     * isNumber(n,'@ != 0');    //判断是否是数字并且!=0
     * isNumber(n,'@ !== 0');   //判断是否是数字并且!==0
     * isNumber(n,'@ > 100 ? "大于100" : "小于等于100"');
     * 
     *   .1    1.  01   01.         不是数字
     *   0.1  1.0   1   1.0   1.00  是数字
     */
    isNumber: function (param, judge) {
        if (typeof param === 'number') {
            if (judge) {
                return judge(param);
            } else {
                return true;
            }
        } else if (typeof param === 'string' && param) {
            if (/^-?([1-9]\d*|[0-9])(\.\d+)?$/.test(param)) {
                if (judge) {
                    return judge(parseFloat(param));
                } else {
                    return true;
                }
            }
        }

        return false;
    },

    /**
     * 判断是否是int型的数字或字符串
     * @param {string|number} param
     * @param {(data:number)=>boolean} judge
     * @returns {Boolean}
     * 
     * exp:
     * isInt(1);        //true
     * isInt(1.0);      //true
     * isInt('1.0');    //false
     */
    isInt: function (param, judge) {
        return number.isNumber(param, function (num) {
            if (judge) {
                return Number.isInteger(num) && judge(num);
            }
            return Number.isInteger(num);
        })
    },

    /**
     * 判断是否是float型的数字或字符串
     * @param {String} param
     * @param {String} judge
     * @returns {Boolean}
     * exp:
     * 
     * isFloat(1);        //false
     * isFloat(1.0);      //false
     * isFloat('1.0');    //true
     */
    isFloat: function (param, judge) {
        return number.isNumber(param, function (num) {
            if (judge) {
                return !Number.isInteger(num) && judge(num);
            }
            return !Number.isInteger(num);
        })
    },

    /**
     * 转成number
     * 
     * @param {String} param
     * @returns {Number}
     */
    toNumber: function (param) {
        var isnum = number.isNumber(param);
        return isnum === false ? Number.NaN : Number.parseFloat(param);
    },

    /**
     * 转成int,非int型返回null
     * 
     * @param {String} param
     * @returns {Number}
     */
    toInt: function (param) {
        var isint = number.isInt(param);
        return isint === false ? Number.NaN : Number.parseInt(param);
    },

    /**
     * 转成float,非float型返回null
     * 
     * @param {String} param
     * @returns {Number}
     */
    toFloat: function (param) {
        var isfloat = number.isFloat(param);
        return isfloat === false ? Number.NaN : Number.parseFloat(param);
    },
    /**
     * 获得某一闭区间的随机数，可以指定小数点位数fixed，fixed有效时对结果4舍5入
     * 
     * @param {Number} min 
     * @param {Number} max 
     * @param {Number} fixed 
     * @returns {Number}
     */
    random: function (min, max, fixed) {
        min = number.toNumber(min);
        max = number.toNumber(max);
        fixed = number.toInt(fixed) || 0;
        if (min === null || max === null || min > max || fixed < 0) {
            throw 'min or max is error param';
        }
        var n;
        if (fixed) {
            n = Math.random() * (max - min) + min;
            n = n.toFixed(fixed);
        } else {
            n = Math.random() * (max - min + 1) + min;
            n = Math.floor(n);
        }

        return number.toNumber(n);
    }
};
var object = {
    /**
     * 判断是否是对象
     * 
     * @param {any} param 
     * @returns {Boolean}
     */
    isObject: function (param) {
        //(typeof null === 'object')为真
        return !!param && (typeof param === 'object') && !(param instanceof Array);
    },
    /**
     * 判断是否是空对象或数组
     * 
     * @param {Object} param
     * @returns {Boolean}
     */
    isEmpty: function (param) {
        if (object.isObject(param)) {
            for (var key in param) {
                if (param.hasOwnProperty(key)) {
                    return false;
                }
            }
            return true;
        }
        return false;
    },
    /**
     * 获取对象或数组的长度
     * 
     * @param {Object} param 
     * @returns {Number}
     */
    getLength: function (param) {
        if (object.isObject(param)) {
            var i = 0;
            for (var key in param) {
                if (param.hasOwnProperty(key)) {
                    i++;
                }
            }
            return i;
        }
        return 0;
    },

    keys: function (param) {
        if (object.isObject(param)) {
            var arr = [];
            for (var key in param) {
                if (param.hasOwnProperty(key)) {
                    arr.push(key);
                }
            }
            return arr;
        }
        return [];
    },

    values: function (param) {
        if (object.isObject(param)) {
            var arr = [];
            for (var key in param) {
                if (param.hasOwnProperty(key)) {
                    arr.push(param[key]);
                }
            }
            return arr;
        }
        return [];
    },

    sortKeys: function (param) {
        if (object.isObject(param)) {
            let index = 0;
            const keys = [];
            for (const key in param) {
                for (index = keys.length - 1; index >= 0; index--) {
                    if (string.compare(key, keys[index]) >= 0) {
                        break;
                    }
                }
                if (index === keys.length - 1) {
                    keys.push(key);
                } else {
                    keys.splice(index + 1, 0, key);
                }
            }
            return keys;
        } else if (array.isArray(param)) {
            return param.map(function (v, k) {
                return k;
            })
        }

        return [];
    },

    /**
     * 返回随机的key数组
     * 
     * @param {Object} param 
     * @returns {[]}
     */
    random: function (param, length) {
        if (object.isObject(param)) {
            var arr = [];
            for (var key in param) {
                if (param.hasOwnProperty(key)) {
                    arr.push(param[key]);
                }
            }
            return array.random(arr, length);
        }
        return [];
    },

    /**
     * 返回随机的key数组
     * 
     * @param {Object} param 
     * @returns {[]}
     */
    randomKeys: function (param, length) {
        if (object.isObject(param)) {
            var arr = [];
            for (var key in param) {
                if (param.hasOwnProperty(key)) {
                    arr.push(key);
                }
            }
            return array.random(arr, length);
        }
        return [];
    }
};
var string = {
    /**
     * 判断是否是字符串
     * 
     * @param {String} param 
     * @returns {Boolean}
     */
    isString: function (param) {
        return typeof param == 'string';
    },
    /**
     * 强转成字符串，不成功返回null
     * 
     * @param {String} param 
     * @returns {String}
     */
    toString: function (param) {
        try {
            return JSON.stringify(param);
        } catch (e) {
            return null;
        }
    },

    /**
     * 比较两字符串的大小
     */
    compare: function (str1, str2) {
        if (str1 === str2) {
            return 0;
        }
        if (str1.length > str2.length) {
            return 1;
        }
        if (str1.length < str2.length) {
            return -1;
        }

        for (let i = 0, code1 = 0, code2 = 0; i < str1.length; i++) {
            if (str2.length <= i) {
                return 1;
            } else {
                code1 = str1.charCodeAt(i);
                code2 = str2.charCodeAt(i);
                if (code1 > code2) {
                    return 1;
                } else if (code1 < code2) {
                    return -1;
                }
            }
        }
        return 0;
    },

    /**
     * 返回指定长度字符串
     * 
     * @param {string|number} i 数字
     * @param {number} length   长度
     * @param {number} b        补截[0-前 1-后]
     * @param {string|number} s 补充用符
     * @returns 
     */
    fixLength: function (i, length, b, s) {
        i = i + '';
        if (arguments.length < 3) {
            b = 0;
        }
        if (arguments.length < 4) {
            s = '0';
        }
        while (i.length < length) {
            if (b == 0) {
                i = s + i;
            } else {
                i = i + s;
            }
        }
        if (i.length > length) {
            if (b == 0) {
                i = i.slice(-length);
            } else {
                i = i.slice(0, length);
            }
        }
        return i;
    },

    /**
     * 格式化字符串
     */
    format: function (str) {
        let index = 0;
        let array = Array.prototype.slice.call(arguments, 1);
        return str.replace(/%s/g, function () {
            return array[index++];
        });
    },

    /**
     * 得到一个随机字符串[大小写字母+数字]
     * @param {Number} len 长度
     * @param {Number} type [1：纯数字；2：纯小写字母；3：纯大写字母；其它：混合]
     */
    random: function (len, type) {
        let r = '';
        let s = '';
        if (type == 1) {
            s = '1234567890';
        } else if (type == 2) {
            s = 'abcdefghijklmnopqrstuvwxyz';
        } else if (type == 3) {
            s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        } else {
            s = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
        }
        for (let i = 0; i < len; i++) {
            r += s.charAt(Math.floor(Math.random() * s.length));
        }
        return r;
    },

    /**
     * 得到一个字符串的字节长度
     * @param {*} str 
     */
    byteSize(str) {
        return new Blob([str]).size;
    }
};

var TYPE = {
    Array: 'ARRAY',
    Boolean: 'BOOL',
    Function: 'FUNCTION',
    Number: 'NUMBER',
    Object: 'OBJECT',
    String: 'STRING'
};

var util = {
    TYPE: {
        ARRAY: 'Array',
        BOOLEAN: 'Boolean',
        FUNCTION: 'Function',
        NUMBER: 'Number',
        OBJECT: 'Object',
        STRING: 'String'
    },
    /**
     * 库：数组类型
     */
    Array: array,
    /**
     * 库：布尔类型
     */
    Boolean: bool,
    /**
     * 库：方法类型
     */
    Function: fun,
    /**
     * 库：数字类型
     */
    Number: number,
    /**
     * 库：对象类型
     */
    Object: object,
    /**
     * 库：字符串类型
     */
    String: string,

    /**
     * 深拷贝 (除function外)
     */
    copy: function (o) {
        return JSON.parse(JSON.stringify(o));
    },

    /**
     * 方法：判断两个参数是否全等
     * 
     * @param {any} param1 
     * @param {any} param2 
     * @returns {Boolean}
     */
    isEqual: function (param1, param2) {
        if (param1 === param2) {
            return true;
        }
        return JSON.stringify(param1) === JSON.stringify(param2);
    },

    /**
     * 方法：判断变量是否存在
     * 
     * @param {any} param 
     * @returns {Boolean}
     */
    isExist: function isExist(param) {
        return typeof param !== 'undefined';
    },

    /**
     * 方法：判断变量是否不存在
     * 
     * @param {any} param 
     * @returns {Boolean}
     */
    isNotExist: function isNotExist(param) {
        return typeof param === 'undefined';
    },

    /**
     * 方法：判断一个对象是否是一个方法的实例
     * 
     * @param {Object} child 
     * @param {funtion} parent 
     * @returns {Boolean}
     */
    isChildOf: function isChildOf(child, parent) {
        return object.isObject(child) && fun.isFunction(parent) && child instanceof parent;
    },

    /**
     * 方法：获得一个参数的类型
     * 
     * @param {any} param 
     * @returns {string}
     */
    getType: function (param) {
        return Object.prototype.toString.call(param).slice(8, -1).toLowerCase();
    },

    /**
     * 方法：判断一个参数是不是指定的类型
     * 
     * @param {any} param 
     * @param {string} type 
     * @returns {Boolean}
     */
    isType: function (param, type) {
        return util.getType(param) === type;
    },

    /**
     * 判断对象内的key是否符合标准
     * 
     * @param {any} param 
     * @returns {error}
     */
    judge: function (object, judge) {
        if (util.Object.isObject(object) && util.Object.isObject(judge)) {
            for (var k in judge) {
                var key = judge[k];
                if (!TYPE[key] || !util[key]['is' + key](object[k])) {
                    console.log('[type] ' + k + '必须是' + key + '类型，而非' + util.getType(object[k]));
                    return false;
                }
            }
            return true;
        }
        console.log('[type] 传入的参数必须是两个对象:' + util.getType(object) + ' ' + util.getType(judge));
        return false;
    },

    /**
     * 将对象内的key强转成指定类型
     * 
     * @param {any} param 
     * @returns {error}
     */
    transform: function (object, judge) {
        if (util.Object.isObject(object) && util.Object.isObject(judge)) {
            for (var k in judge) {
                var key = judge[k];
                if (TYPE[key]) {
                    try {
                        if (util[key]['is' + key](object[k])) {
                            object[k] = util[key]['to' + key](object[k]);
                        } else {
                            console.log('[type] 转换失败 ' + k + '不是' + key + '类型');
                            return false;
                        }
                    } catch (e) {
                        console.log('[type] 转换失败 ' + e.toString());
                        return false;
                    }
                }
            }
            return true;
        }
        console.log('[type] 传入的参数必须是两个对象:' + util.getType(object) + ' ' + util.getType(judge));
        return false;
    },

    /**
     * 可遍历数组和对象 [遍历数组过程中，可安全的删除当前遍历项]
     */
    forEach: function (param, cb) {
        if (array.isArray(param)) {
            for (let index = 0; index < param.length; index++) {
                const value = param[index];
                if (cb(value, index, param) === false) {
                    return index;
                }
                if (param[index] !== value) {
                    index--;
                }
            }
        } else if (object.isObject(param)) {
            for (const key in param) {
                if (param.hasOwnProperty(key)) {
                    if (cb(param[key], key, param) === false) {
                        return key;
                    }
                }
            }
        }
    }
};

module.exports = util;